#include "iConfig.hpp"

#include <glf/core/readWriteMutexLock.h>
#include "iThread.h"

namespace glf {

ReadWriteMutexLock::ReadWriteMutexLock()
: CanReadCondition(CountersMutex)
, CanWriteCondition(CountersMutex)
, Readers(0)
, Writers(0)
, WriteAccessOwner(0)
{
}

ReadWriteMutexLock::~ReadWriteMutexLock() {
	GLF_ASSERT(Readers == 0);
	GLF_ASSERT(Writers == 0);
}

// (Get Shared Read Lock) attempts to acquire a shared read lock on the 
// read/write lock specified by rwlock.
int ReadWriteMutexLock::readLock(unsigned int time)
{
	LockScope lock(CountersMutex);

	if(WriteAccessOwner == GetCurrentThreadId()) {
		Readers++;
		return 1;
	}

	//GLITCH_TODO_MESSAGE("We should decrease the timer at each loop if the start value is not 0");
	while(true) {
		if(Writers != 0) {
			if(CanReadCondition.Wait(time) == 0) {
				// Time elapsed
				return 0; // Failed
			}
			// Even if the condition is signaled, the count could be back to 1
			// so we will loop again just to be sure that it is set to 0
		} else {
			Readers++;
			return 1; // Succeed
		}
	} 
}

// (Get Exclusive Write Lock) attempts to acquire an exclusive write lock 
// on the read/write lock specified by rwlock.
int ReadWriteMutexLock::writeLock(unsigned int time) {
	LockScope lock(CountersMutex);
	if(WriteAccessOwner == GetCurrentThreadId()) {
		Writers++;
		return 1;
	}

	//GLITCH_TODO_MESSAGE("We should decrease the timer at each loop if the start value is not 0");
	while(true) {
		if(Readers != 0 || Writers != 0) {
			if(CanWriteCondition.Wait(time) == 0) {
				// Time elapsed
				return 0; // Failed
			}
			// Even if the condition is signaled, the count could be back to 1
			// so we will loop again just to be sure that it is set to 0
		} else {
			WriteAccessOwner = GetCurrentThreadId();
			Writers++;
			return 1; // Succeed
		}
	} 
}

// (Get Shared Read Lock with No Wait) attempts to acquire a shared read 
// lock on the read/write lock specified by rwlock.
int ReadWriteMutexLock::tryReadLock() {
	LockScope lock(CountersMutex);

	if(WriteAccessOwner == GetCurrentThreadId()) {
		Readers++;
		return 1;
	}

	if(Writers != 0) {
		return 0; // Failed
	} else {
		Readers++;
		return 1; // Succeed
	}
}

// (Get Exclusive Write Lock with No Wait) attempts to acquire an exclusive 
// write lock on the read/write lock specified by rwlock.
int ReadWriteMutexLock::tryWriteLock() {
	LockScope lock(CountersMutex);

	if(WriteAccessOwner == GetCurrentThreadId()) {
		Writers++;
		return 1;
	}

	if(Readers != 0 || Writers != 0) {
		return 0; // Failed
	} else {
		WriteAccessOwner = GetCurrentThreadId();
		Writers++;
		return 1; // Succeed
	}
}

// (Shared Read Lock) unlocks a shared read held by the calling thread.
int ReadWriteMutexLock::readUnlock() {
	LockScope lock(CountersMutex);
	GLF_ASSERT(Readers > 0);
	Readers--;
	if(Readers == 0 && Writers == 0) {
		CanWriteCondition.Signal();
	}

	return 1; // Succeed
}

// (Unlock Exclusive Write) unlocks an exclusive write lock held by the calling thread.
int ReadWriteMutexLock::writeUnlock() {
	LockScope lock(CountersMutex);
	GLF_ASSERT(Writers > 0);
	GLF_ASSERT(WriteAccessOwner == GetCurrentThreadId());
	Writers--;
	if(Writers == 0) {
		WriteAccessOwner = 0;
		CanWriteCondition.Signal();
		CanReadCondition.Broadcast();
	}

	return 1; // Succeed
}

} // namespace glf
